package com.ugoodtech.mdcc.core.service.impl;


import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
import com.drew.imaging.jpeg.JpegProcessingException;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.MetadataException;
import com.drew.metadata.exif.ExifIFD0Directory;
import com.mysema.query.BooleanBuilder;
import com.ugoodtech.mdcc.core.domain.Photo;
import com.ugoodtech.mdcc.core.domain.QPhoto;
import com.ugoodtech.mdcc.core.repository.PhotoRepository;
import com.ugoodtech.mdcc.core.service.PhotoService;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.imgscalr.Scalr;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

//import javax.annotation.PostConstruct;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.Date;
import java.util.List;


/**
 * Copyright © 2013 All Rights Reserved, Ugood Technology, Inc.
 */
@Service("photoService")
public class PhotoServiceImpl implements PhotoService {


    @Autowired
    private PhotoRepository photoRepository;


    @Value("${photos.basedir}")
    private String baseDir;

    private String photoBaseDir;


//    @PostConstruct
    public void initPhotoDir() {
//        System.out.println("baseDir="+baseDir);
        File baseDirFile = new File(baseDir);
        boolean ready = true;

        if (!baseDirFile.exists()) {
            ready = baseDirFile.mkdirs();
        }
        if (!ready) {
            log.error("couldn't create base dir:" + baseDir);
            return;
        }
        //create  user Photo Base Dir
        photoBaseDir = baseDir + File.separator + "file";
        File userPhotoBaseDir = new File(photoBaseDir);
        if (!userPhotoBaseDir.exists()) {
            ready = userPhotoBaseDir.mkdirs();
        }
        if (!ready) {
            log.error("couldn't create users' photo base dir:" + photoBaseDir);
        }

    }

    @Autowired
    public void setPhotoRepository(PhotoRepository photoRepository) {
        this.photoRepository = photoRepository;
    }


    @Override
    public Photo savePhotoFile(byte[] photoBytes, String fileName, Long categoryId) throws IOException {

        Photo photo = new Photo();
        photo.setCreationTime(new Date());
        photo.setName(fileName);
        try {
            String prefix = fileName.substring(fileName.lastIndexOf(".") + 1);
            prefix = prefix.toLowerCase();
            photo.setMimeType(prefix);
        } catch (Exception e) {
            e.printStackTrace();
        }
//        if (categoryId > 100l) {
//            photo.setCategoryCode(categoryId);
////            if(categoryId>100000){
////                Long departmentId=categoryId/10000;
////                Long productId=(categoryId/100)%100;
////                Long nodeId=categoryId%100;
////                String department=categoryRepository.findOne(departmentId).getTitle();
////                String product=categoryRepository.findOne(productId).getTitle();
////                String node=categoryRepository.findOne(nodeId).getTitle();
////            }
//        } else {
//            Category category = categoryRepository.findOne(categoryId);
//            photo.setCategory(category);
//        }
        photoRepository.save(photo);
        //

        File photoFile = new File(photoBaseDir, photo.getId().toString() + "_" + fileName);
        FileUtils.writeByteArrayToFile(photoFile, photoBytes);
//        rotatePhonePhoto(photoBaseDir+photo.getId().toString(),
//                getRotateAngleForPhoto(photoBaseDir+photo.getId().toString()));
        rotatePhonePhoto(photoBaseDir + File.separator + photo.getId().toString() + "_" + fileName.toString(),
                getRotateAngleForPhoto(photoBaseDir + File.separator + photo.getId().toString() + "_" + fileName));
        return photo;
    }

    @Override
    public Page<Photo> queryFiles(List<String> mimeTypes, List<String> notInMimeTypes, Long categoryId, Pageable pageable, String name, Boolean enabled) {
        QPhoto qPhoto = QPhoto.photo;
        BooleanBuilder builder = new BooleanBuilder();
        builder.and(qPhoto.deleted.eq(false));
//        builder.and(qPhoto.enabled.eq(true));
        if (!StringUtils.isEmpty(name)) {
            builder.and(qPhoto.name.like("%" + name + "%"));
        }
        if (null != enabled) {
            builder.and(qPhoto.enabled.eq(enabled));
        }
//        if (null != categoryId) {
////            if (categoryId>100l) {
////                builder.and(qPhoto.categoryCode.eq(categoryId));
////            } else {
//            builder.and(qPhoto.category.id.eq(categoryId));
////            }
//        }
        if (null != mimeTypes && mimeTypes.size() > 0) {
            builder.and(qPhoto.mimeType.in(mimeTypes));
        }
        if (null != notInMimeTypes && notInMimeTypes.size() > 0) {
            builder.and(qPhoto.mimeType.notIn(notInMimeTypes));
        }


        if (pageable.getSort() == null) {
            Sort sort = new Sort(Sort.Direction.DESC, "creationTime");
            pageable = new PageRequest(pageable.getPageNumber(), pageable.getPageSize(), sort);
        }
        return photoRepository.findAll(builder, pageable);
    }

    @Override
    public byte[] getDefaultPhoto(String name) throws IOException {
        File photoFile = new File(photoBaseDir, name);
        return FileUtils.readFileToByteArray(photoFile);
    }


    @Override
    public byte[] getPhotoBytes(Long photoId) throws IOException {
        Photo photo = photoRepository.findOne(photoId);
        String fileName = photo.getName();
        File photoFile = new File(photoBaseDir, photoId.toString() + "_" + fileName);
        return FileUtils.readFileToByteArray(photoFile);
    }


    public static int getRotateAngleForPhoto(String filePath) {

        File file = new File(filePath);

        int angle = 0;

        Metadata metadata;
        try {
            metadata = ImageMetadataReader.readMetadata(file);
            Directory directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
            if (null != directory) {
                if (directory.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) {

                    // Exif信息中方向　　
                    int orientation = directory.getInt(ExifIFD0Directory.TAG_ORIENTATION);

                    // 原图片的方向信息
                    if (6 == orientation) {
                        //6旋转90
                        angle = 90;
                    } else if (3 == orientation) {
                        //3旋转180
                        angle = 180;
                    } else if (8 == orientation) {
                        //8旋转90
                        angle = 270;
                    }
                }
            }
        } catch (JpegProcessingException e) {
            e.printStackTrace();
        } catch (MetadataException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ImageProcessingException e) {
            e.printStackTrace();
        }

        return angle;
    }

    public static String rotatePhonePhoto(String fullPath, int angel) {

        BufferedImage src;
        try {
            src = ImageIO.read(new File(fullPath));

            int src_width = src.getWidth(null);
            int src_height = src.getHeight(null);

            Rectangle rect_des = CalcRotatedSize(new Rectangle(new Dimension(src_width, src_height)), angel);
            BufferedImage res = new BufferedImage(rect_des.width, rect_des.height, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2 = res.createGraphics();

            g2.translate((rect_des.width - src_width) / 2,
                    (rect_des.height - src_height) / 2);
            g2.rotate(Math.toRadians(angel), src_width / 2, src_height / 2);

            g2.drawImage(src, null, null);

            ImageIO.write(res, "jpg", new File(fullPath));

        } catch (IOException e) {

            e.printStackTrace();
        }

        return fullPath;

    }

    public static Rectangle CalcRotatedSize(Rectangle src, int angel) {
        if (angel >= 90) {
            if (angel / 90 % 2 == 1) {
                int temp = src.height;
                src.height = src.width;
                src.width = temp;
            }
            angel = angel % 90;
        }
        double r = Math.sqrt(src.height * src.height + src.width * src.width) / 2;
        double len = 2 * Math.sin(Math.toRadians(angel) / 2) * r;
        double angel_alpha = (Math.PI - Math.toRadians(angel)) / 2;
        double angel_dalta_width = Math.atan((double) src.height / src.width);
        double angel_dalta_height = Math.atan((double) src.width / src.height);
        int len_dalta_width = (int) (len * Math.cos(Math.PI - angel_alpha
                - angel_dalta_width));
        int len_dalta_height = (int) (len * Math.cos(Math.PI - angel_alpha
                - angel_dalta_height));
        int des_width = src.width + len_dalta_width * 2;
        int des_height = src.height + len_dalta_height * 2;
        return new Rectangle(new Dimension(des_width, des_height));
    }


    public byte[] getPhotoThumbnailBytesOld(Long photoId, int width, int height) throws IOException, PhotoNotFoundException {
        String photoFilePath = photoBaseDir +
//                File.separator + photoId.toString() +
                File.separator + (width + "_" + height) +
                File.separator + photoId;
        File photoFile = new File(photoFilePath);
        if (photoFile.exists()) {
            return FileUtils.readFileToByteArray(photoFile);
        } else {
            File originalPhotoFile = new File(photoBaseDir + File.separator + photoId);
            if (originalPhotoFile.exists()) {
//                BufferedImage imgSrc = ImageIO.read(originalPhotoFile);
//                BufferedImage targetImg = Scalr.resize(imgSrc, Scalr.Method.SPEED, Scalr.Mode.FIT_TO_WIDTH, width, height,
//                        Scalr.OP_ANTIALIAS);
//                BufferedImage targetImg = Scalr.resize(imgSrc, Scalr.Method.SPEED, Scalr.Mode.FIT_EXACT, width, height,
//                        Scalr.OP_ANTIALIAS);
//
//                BufferedImage targetImg;
                BufferedImage imgSrc = ImageIO.read(originalPhotoFile);
                BufferedImage newImage = new BufferedImage(imgSrc.getWidth(), imgSrc.getHeight(), BufferedImage.TYPE_3BYTE_BGR);

                for (int x = 0; x < imgSrc.getWidth(); x++) {
                    for (int y = 0; y < imgSrc.getHeight(); y++) {
                        newImage.setRGB(x, y, imgSrc.getRGB(x, y));
                    }
                }
                BufferedImage targetImg;
//                while(width>imgSrc.getWidth()||height>imgSrc.getHeight()){
//                    width/=2;
//                    height/=2;
//                }
                if (imgSrc.getWidth() * height < width * imgSrc.getHeight()) {
                    targetImg = Scalr.resize(imgSrc, Scalr.Method.BALANCED, Scalr.Mode.FIT_TO_WIDTH, width, height,
                            Scalr.OP_ANTIALIAS);

                    targetImg = targetImg.getSubimage(0, Math.abs(targetImg.getHeight() - height) / 2, width, height);
                } else {
                    targetImg = Scalr.resize(imgSrc, Scalr.Method.BALANCED, Scalr.Mode.FIT_TO_HEIGHT, width, height,
                            Scalr.OP_ANTIALIAS);
                    targetImg = targetImg.getSubimage(Math.abs(targetImg.getWidth() - width) / 2, 0, width, height);
                }
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ImageIO.write(targetImg, "jpg", baos);
                baos.flush();
                byte[] imageInByte = baos.toByteArray();
                baos.close();
                FileUtils.writeByteArrayToFile(photoFile, imageInByte);
                return imageInByte;
            } else {
                log.error("photo not found with id:" + photoId);
                throw new PhotoNotFoundException("photo not found with id:" + photoId);
            }
        }
    }


    @Override
    public byte[] getPhotoThumbnailBytes(Long photoId, int width, int height) throws IOException, PhotoNotFoundException {
        String photoFilePath = photoBaseDir +
//                File.separator + photoId.toString() +
                File.separator + (width + "_" + height) +
                File.separator + photoId + ".jpg";
        System.out.println(photoFilePath);
        File photoFile = new File(photoFilePath);
        if (photoFile.exists()) {
            return FileUtils.readFileToByteArray(photoFile);
        } else {
//            File originalPhotoFile = new File(photoBaseDir + File.separator + photoId);
            Photo photo = photoRepository.findOne(photoId);
            String fileName = photo.getName();
            File originalPhotoFile = new File(photoBaseDir, photoId.toString() + "_" + fileName);
            if (originalPhotoFile.exists()) {


                //计算原图宽度
                BufferedImage buff = ImageIO.read(new FileInputStream(originalPhotoFile));
                BufferedImage bufferedImage;
                int originalWidth = buff.getWidth();
                int originalHeight = buff.getHeight();
                File parent = photoFile.getParentFile();
                if (parent != null && !parent.mkdirs() && !parent.isDirectory()) {
                    throw new IOException("Directory \'" + parent + "\' could not be created");
                }
                //如果实际宽度大于需求宽度才缩放
                if (originalWidth > width) {
                    NumberFormat numberFormat = NumberFormat.getInstance();
                    // 设置精确到小数点后2位
                    numberFormat.setMaximumFractionDigits(2);
                    String percent = numberFormat.format((float) width / (float) originalWidth);
                    double percentDouble = new Double(percent);


//                    Thumbnails.of(originalPhotoFile)
//                            .scale(percentDouble)
//                            .outputQuality(1.0)
//                            .toFile(photoFile);

                    //按百分比缩放
                    bufferedImage = Thumbnails.of(originalPhotoFile)
                            .scale(percentDouble)
                            .outputQuality(1.0)
                            .outputFormat("jpg").asBufferedImage();
                    //如果缩放完高度大于需求高度才剪裁
                    if (bufferedImage.getHeight() > height) {
                        //按缩放后宽度与需求高度剪裁
                        Thumbnails.of(bufferedImage)
                                .scale(1.0)
                                .outputQuality(1.0)
                                .sourceRegion(0, 0, bufferedImage.getWidth(), height).toFile(photoFile);
                    } else {
                        //返回缩放后图
                        Thumbnails.of(bufferedImage)
                                .scale(1.0)
                                .outputQuality(1.0)
                                .toFile(photoFile);
                    }
                    return FileUtils.readFileToByteArray(photoFile);
                } else {
                    if (originalHeight > height) {
                        bufferedImage = Thumbnails.of(originalPhotoFile)
                                .scale(1)
                                .outputQuality(1.0)
                                .outputFormat("jpg").asBufferedImage();
                        Thumbnails.of(bufferedImage)
                                .scale(1.0)
                                .outputQuality(1.0)
                                .sourceRegion(0, 0, originalWidth, height).toFile(photoFile);
                        return FileUtils.readFileToByteArray(photoFile);

                    } else {
                        //返回原图
                        return FileUtils.readFileToByteArray(originalPhotoFile);
                    }
                }


            } else {
                log.error("photo not found with id:" + photoId);
                throw new PhotoNotFoundException("photo not found with id:" + photoId);
            }
        }
    }

    public byte[] getPhotoThumbnailBytesBackup(Long photoId, int width, int height) throws IOException, PhotoNotFoundException {
        String photoFilePath = photoBaseDir +
//                File.separator + photoId.toString() +
                File.separator + (width + "_" + height) +
                File.separator + photoId + ".jpg";
        System.out.print(photoFilePath);
        File photoFile = new File(photoFilePath);
        if (photoFile.exists()) {
            return FileUtils.readFileToByteArray(photoFile);
        } else {
            File originalPhotoFile = new File(photoBaseDir + File.separator + photoId);
            if (originalPhotoFile.exists()) {
                BufferedImage imgSrc = ImageIO.read(originalPhotoFile);
                BufferedImage targetImg = null;
//                targetImg=this.filter(imgSrc,targetImg);
                File parent = photoFile.getParentFile();
                if (parent != null && !parent.mkdirs() && !parent.isDirectory()) {
                    throw new IOException("Directory \'" + parent + "\' could not be created");
                }

                if (imgSrc.getWidth() * height < width * imgSrc.getHeight()) {
                    targetImg = Thumbnails.of(originalPhotoFile).size(width, (int) ((width * 1.0 / imgSrc.getWidth() * 1.0) * imgSrc.getHeight())).imageType(2).outputFormat("jpg").asBufferedImage();

                    if (targetImg.getWidth() < width) {
                        targetImg = targetImg.getSubimage(0, 0, targetImg.getWidth(), height);
                    } else {
                        targetImg = targetImg.getSubimage(Math.abs(targetImg.getWidth() - width) / 2, 0, width, height);
                    }
                    Thumbnails.of(targetImg).size(width, height).imageType(2).outputFormat("jpg").toFile(photoFile);
                } else {
                    targetImg = Thumbnails.of(originalPhotoFile).size((int) (height * 1.0 / imgSrc.getHeight() * 1.0 * imgSrc.getWidth()), height).imageType(2).outputFormat("jpg").asBufferedImage();
                    if (targetImg.getHeight() < height) {
                        targetImg = targetImg.getSubimage(Math.abs(targetImg.getWidth() - width) / 2, 0, width, targetImg.getHeight());
                    } else {
                        targetImg = targetImg.getSubimage(Math.abs(targetImg.getWidth() - width) / 2, 0, width, height);
                    }
                    Thumbnails.of(targetImg).size(width, height).imageType(2).outputFormat("jpg").toFile(photoFile);
                }

                return FileUtils.readFileToByteArray(photoFile);

            } else {
                log.error("photo not found with id:" + photoId);
                throw new PhotoNotFoundException("photo not found with id:" + photoId);
            }
        }
    }

    public BufferedImage filter(BufferedImage src, BufferedImage dst) {
        if (dst == null) {
            ColorModel dstCM = src.getColorModel();
            dst = new BufferedImage(dstCM, dstCM.createCompatibleWritableRaster(dst.getWidth(), dst.getHeight()),
                    dstCM.isAlphaPremultiplied(), null);
        }

        Image scaleImage = src.getScaledInstance(dst.getWidth(), dst.getHeight(), Image.SCALE_SMOOTH);
        Graphics2D g = dst.createGraphics();
        g.drawImage(scaleImage, 0, 0, dst.getWidth(), dst.getHeight(), null);
        g.dispose();

        return dst;
    }


    @Override
    public Photo getPhotoById(Long photoId) {
        return photoRepository.findOne(photoId);
    }

//    @Override
//    public String createQRCode(String contents) {
//        Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>();
//        hints.put(EncodeHintType.CHARACTER_SET, "GBK");
//        BitMatrix matrix = null;
//        try {
//            matrix = new MultiFormatWriter().encode(contents, BarcodeFormat.QR_CODE, 200, 200, hints);
//        } catch (WriterException e) {
//            e.printStackTrace();
//
//        }
//        String fileName="QR"+new Date().getTime()+".png";
//        String originPath = baseDir + "image/QRCode/" + fileName; //文件夹
//        //todo
//        String originUrl = serviceUrl +"/photo/getByteByUrl?url="+ baseDir + "image/QRCode/" + fileName;
//        File file = new File(originPath);
//        try {
//            BaseUtil.createFile(file);
//            MatrixToImageWriter.writeToFile(matrix, "png", file);
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
////        System.out.println(originPath);
////        System.out.println(originUrl);
//        return originUrl;
//    }


    @Override
    public Photo save(Photo photo) {
        return photoRepository.save(photo);
    }


    private Logger log = LoggerFactory.getLogger(PhotoServiceImpl.class);
}
